﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "WallCalculator.h"
#include "IGenericElement.h"
#include "IGraphicsElementShape.h"
#include "IElementModelShape.h"
#include "ILine3d.h"
#include "IPolygon.h"
#include "Vector2dUtils.h"
#include "IPolyCurve.h"
#include "ILine2d.h"
#include "GmBodyBuilder.h"
#include "IGraphicsBRepBody.h"
#include "IExternalObject.h"
#include "WallPositionPoints.h"
#include "ICategoryLibrary.h"
#include "ICategory.h"
#include "GraphicsNodeUtils.h"
#include "Wall.h"
#include "IGraphicsMaterialManager.h"
#include "CategoryDefine.h"
#include "IGraphicsMaterial.h"
#include "IGraphicsStyle.h"
#include "IElementBasicInformation.h"
#include "IGeometryRelationshipComponent.h"
#include "ICutRelationshipBehavior.h"
#include "IElementCut.h"
#include "RegenDataId.h"
#include "AlgorithmBodyTopologyOperate.h"
#include "ICutterGraphicsElementShapeComponent.h"
#include "CutUtils.h"
#include "IGraphicsPoint.h"
#include "IGraphicsBRepFace.h"
#include "IGraphicsBRepEdge.h"
#include "../UIPlatform/DBUtils.h"
#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

using namespace gcmp;
using namespace Sample;

IGraphicsMaterial* FindGraphicsMaterial(IDocument* pDoc, const std::wstring& instanceName)
{
    std::vector<IGraphicsMaterial*> vecGraphicsMaterial = IGraphicsMaterial::GetAllGraphicsMaterials(pDoc);
    for (auto ele : vecGraphicsMaterial)
    {
        if (!ele->IsBoundWithMaterialInstance())
        {
            continue;
        }
        std::wstring strName;
        if (!ele->GetMaterialInstanceName(strName))
        {
            continue;
        }
        if (strName == instanceName)
        {
            return ele;
        }
    }
    return nullptr;
}

IGraphicsStyle* FindGraphicsStyle(IDocument* pDoc, const std::wstring& name)
{
    std::vector<IGraphicsStyle*> allGraphicsStyle = IGraphicsStyle::GetAllGraphicsStyles(pDoc);
    FOR_EACH(pStyle, allGraphicsStyle)
    {
        IElementBasicInformation* pElementBasicInfo = pStyle->GetBasicInformation();
        if (pElementBasicInfo->GetName() == name)
        {
            return pStyle;
        }
    }
    return nullptr;
}

void WallGraphicsElementShapeCalculator::ReportInputDataIds(std::vector<RegenDataId>& oInputDatas) const
{
    IDocument* pDocument = GetDocument();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pDocument, L"Document为空", L"GDMP", L"2023-11-08");
    const IGenericElement* pGenericElement = quick_cast<const IGenericElement>(
        pDocument->GetElement(ElementId(GetRegenDataId().ObjectId)));
    DBG_WARN_AND_RETURN_VOID_UNLESS(pGenericElement, L"GenericElement不能为空", L"GDMP", L"2023-09-30");
    const Wall* pWall = dynamic_cast<const Wall*>(pGenericElement->GetExternalObject());
    DBG_WARN_AND_RETURN_VOID_UNLESS(pWall, L"pWall不能为空", L"GDMP", L"2023-09-30");
    oInputDatas.emplace_back(pWall->GetDataRdId());

    // 剪切计算所需要的RdId
    const IGeometryRelationshipComponent* pGeometryRepCpt = pGenericElement->GetGeometryRelationshipComponent();
    if (pGeometryRepCpt && pGeometryRepCpt->GetCutRelationship())
    {
        const ICutRelationshipBehavior* pCutRelationship = pGeometryRepCpt->GetCutRelationship();
        oInputDatas.push_back(pCutRelationship->GetCutElementIdsRdId());
        std::vector<RegenDataId> cutRdIds = pCutRelationship->GetElementCutRdIds();
        oInputDatas.insert(oInputDatas.end(), cutRdIds.begin(), cutRdIds.end());
    }
}

void WallGraphicsElementShapeCalculator::Execute()
{
    IGenericElement *pGenericElement = quick_cast<IGenericElement>(
        GetDocument()->GetElement(ElementId(GetRegenDataId().ObjectId)));
    DBG_WARN_AND_RETURN_VOID_UNLESS(pGenericElement, L"GenericElement不能为空", L"GDMP", L"2023-09-30");
    Wall* pWall = quick_cast<Wall>(pGenericElement->GetExternalObject());
    DBG_WARN_AND_RETURN_VOID_UNLESS(pWall, L"pWall不能为空", L"GDMP", L"2023-09-30");

    OwnerPtr<IBody> opBody = pWall->MakeBody();
    OwnerPtr<IGraphicsBRepBody> opBrepBody = IGraphicsBRepBody::Create(TransferOwnership(opBody));

    std::vector<const IGraphicsElementShape*> cutterGReps;
    const IGeometryRelationshipComponent* pGeometryRepCpt = pGenericElement->GetGeometryRelationshipComponent();
    if (pGeometryRepCpt == nullptr)
        return;
    // 获取所有扣减对象，如果和被扣减对象有相交，获取它们的GraphicsElementShape
    const std::vector<ElementId> &cutIds = pGeometryRepCpt->GetCutRelationship()->GetElementCutIds();
    if (cutIds.empty())
    {
        //增加拾取边和面的能力
        int AttachingFaceCount = opBrepBody->GetFaceCount();
        for (int faceIndex = 0; faceIndex < AttachingFaceCount; ++faceIndex)
        {
            IGraphicsBRepFace* thisGrepFace = opBrepBody->GetFaceFW(faceIndex);
            thisGrepFace->SetId(GraphicsNodeId(faceIndex + 1));
        }
        int AttachingEdgeCount = opBrepBody->GetEdgeCount();
        for (int edgeIndex = 0; edgeIndex < AttachingEdgeCount; ++edgeIndex)
        {
            IGraphicsBRepEdge* thisGrepEdge = opBrepBody->GetEdgeFW(edgeIndex);
            thisGrepEdge->SetId(GraphicsNodeId(edgeIndex + 1));
        }
        //赋予新材质
        ElementId materialElementId = ElementId::InvalidID;
        IGraphicsMaterialManager* pGraphicsMaterialManager = GetDocument()->GetGraphicsMaterialManager();
        if (pGraphicsMaterialManager)
        {
            const gcmp::ICategory* pCategory = ICategoryLibrary::Get(pWall->GetDocument())->GetCategory(WALL_CATEGORY);
            if (pCategory)
            {
                materialElementId = pGraphicsMaterialManager->GetGraphicsMaterialIdByCategoryUid(pCategory->GetUniIdentity());
                if (gcmp::ElementId::InvalidID == materialElementId)
                {
                    IGraphicsMaterial* pMaterial = FindGraphicsMaterial(GetDocument(), WALL_MATERIAL_NAME);
                    if (pMaterial)
                    {
                        opBrepBody->SetGraphicsMaterialId(pMaterial->GetElementId());
                    }
                }
            }
        }
        IGraphicsStyle* pStyle = FindGraphicsStyle(GetDocument(), WALL_STYLE_NAME);
        if (pStyle)
        {
             opBrepBody->SetGraphicsStyleId(pStyle->GetElementId());
        }

        gcmp::OwnerPtr<IGraphicsPoint> opPoint = IGraphicsPoint::Create(pWall->GetData()->GetStartPoint());
        opPoint->SetVisibility(GraphicsNodeVisibility::HighlightedExactly_Or_SelectedExactly_Or_RootGRepHighlighted);

        OwnerPtr<IGraphicsElementShape> opGraphicsElementShape =
            IGraphicsElementShape::Create(GraphicsRenderLayer::Model);
        opGraphicsElementShape->SetElementId(pGenericElement->GetElementId());
        opGraphicsElementShape->AddChild(TransferOwnership(opBrepBody));

        opGraphicsElementShape->AddChild(TransferOwnership(opPoint));

        pGenericElement->GetElementModelShape()->SetGraphicsElementShape(TransferOwnership(opGraphicsElementShape));
        return;
    }
    OwnerPtr<IGraphicsElementShape> opGraphicsElementShape =
        IGraphicsElementShape::Create(GraphicsRenderLayer::Model);
    opGraphicsElementShape->SetElementId(pGenericElement->GetElementId());
    opGraphicsElementShape->AddChild(TransferOwnership(opBrepBody));

    // 获取被扣减对象的GraphicsElementShape
    IGraphicsElementShape* pCutteeGRep = opGraphicsElementShape.get();
    OwnerPtr<IGraphicsElementShape> opCutteeGRep = IGraphicsElementShape::Create(pCutteeGRep);
    Box3d cutteeBox = GraphicsNodeUtils::CalculateGraphicsNodeBoundingBox(GetDocument(), opCutteeGRep.get());
    opCutteeGRep->SetBox(cutteeBox);

    ElementId ownerElementId = pGenericElement->GetElementId();
    FOR_EACH(cutId, cutIds)
    {
        IElementCut *pElementCut = quick_cast<IElementCut>(GetDocument()->GetElement(cutId));
        if (!pElementCut)
            continue;
        if (!pElementCut->IsCutter(ownerElementId))
        {
            IElement *pCutter = GetDocument()->GetElement(pElementCut->GetCutterId());
            DBG_WARN_AND_RETURN_VOID_UNLESS(pCutter, L"无效pCutting.", L"GDMP", L"2023-09-30");
            const IGeometryRelationshipComponent* pCutterGeometryRepCpt = pCutter->GetGeometryRelationshipComponent();
            if (!pCutterGeometryRepCpt)
                return;

            const IGraphicsElementShape* pCutterGRep = nullptr;
            if (pCutterGeometryRepCpt->GetCutterGraphicsElementShapeComponent())
            {
                pCutterGRep = pCutterGeometryRepCpt->GetCutterGraphicsElementShapeComponent()->GetCutterGraphicsElementShape();
            }

            DBG_WARN_AND_RETURN_VOID_UNLESS(pCutterGRep, L"无效pCutGRep.", L"GDMP", L"2023-09-30");
            if (DBUtils::IsIntersect(GetDocument(), pCutterGRep, opCutteeGRep.get()))
            {
                cutterGReps.push_back(pCutterGRep);
            }
        }
    }

    // 如果没有扣减体，不做扣减
    if (cutterGReps.empty())
    {
        pGenericElement->GetElementModelShape()->SetGraphicsElementShape(TransferOwnership(opGraphicsElementShape));
        return;
    }

    // 获取被扣减对象的GraphicsElementShape，和所有扣减对象的GraphicsElementShape做布尔扣减，并将最后扣减结果设置回被扣减对象
    OwnerPtr<IGraphicsBRepBody> opCutteeBRep = DBUtils::GetGRepBody(GetDocument(), opCutteeGRep.get());
    const IBody* pCutteeBody = opCutteeBRep->GetGeometry();
    OwnerPtr<IBody> opNewBody = nullptr;
    FOR_EACH(cutterGrep, cutterGReps)
    {
        OwnerPtr<IGraphicsBRepBody> opCutterBRep = DBUtils::GetGRepBody(GetDocument(), cutterGrep);
        const IBody* pCutterBody = opCutterBRep->GetGeometry();
        opNewBody = AlgorithmBodyTopologyOperate::BoolOperate(pCutteeBody, pCutterBody, BodyBooleanOperation::Substract);
    }
    OwnerPtr<IGraphicsBRepBody> opNewBRepBody = IGraphicsBRepBody::Create(TransferOwnership(opNewBody));
    OwnerPtr<IGraphicsElementShape> opGRep = IGraphicsElementShape::Create(GraphicsRenderLayer::Model);
    opGRep->AddChild(TransferOwnership(opNewBRepBody));
    pGenericElement->GetElementModelShape()->SetGraphicsElementShape(TransferOwnership(opGRep));
}

WallGraphicsElementShapeCalculator::WallGraphicsElementShapeCalculator(gcmp::IDocument* pDocument, const RegenDataId& outputDataId)
    : m_pDocument(pDocument)
    , m_outputDataId(outputDataId)
{

}

std::wstring WallGraphicsElementShapeCalculator::GetCalculatorName() const
{
    return L"WallGraphicsElementShapeCalculator";
}

gcmp::IDocument* WallGraphicsElementShapeCalculator::GetDocument() const
{
    return m_pDocument;
}

const RegenDataId& WallGraphicsElementShapeCalculator::GetRegenDataId() const
{
    return m_outputDataId;
}

